"""


2. 通过 file id， 获取文件 or 图片
3. 获取 相册列表
"""
import os.path
import os
import zipfile
import time
import uuid
from flask import current_app, Response
from flask import g
from flask import request
from flask import safe_join, send_file
from flask_restplus import Resource, Namespace, fields, marshal
from app.user.decorators import token_required, permission_required, token_required_func
from common.decorators import catch_error
from common.request_parser import get_parser as base_get_parser
from app.file.model import File
from common.response_model import base_model, pagination_model
from common import constant
from common.responses import ok, bad_request, not_found
from flask_restplus import Resource, Namespace, fields, marshal, reqparse
from common import responses, errors
from app.file.model import FileRelation
from app.user.controller import simple_user_model
from exts import db
from app.operation_log.model import OperationLog

api = Namespace('file', path='/file')

ALLOWED_IMG_EXTENSIONS = {'jpg', 'png', 'jpeg'}
ALLOWED_BINARY_EXTENSIONS = {'zip', 'jpg', 'png', 'jpeg', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'txt', 'rar', '7z'}


def format_size(bytes_str):
    try:
        bytes_str = float(bytes_str)
        kb = bytes_str / 1024
    except:
        print("传入的字节格式不对")
        return "Error"

    if kb >= 1024:
        M = kb / 1024
        if M >= 1024:
            G = M / 1024
            return "%fG" % (G)
        else:
            return "%fM" % (M)
    else:
        return "%fkb" % (kb)


# model
simple_file_model = base_model.inherit('simple_file_model', {
    'url': fields.String(
        attribute=lambda ele: constant.IMAGE_URL_BASE.format(str(ele.id), ele.name) if ele else ""),
})

file_model = simple_file_model.inherit('file_model', {
    'file_path': fields.String(),
    'file_type': fields.String(),
    "create_datetime": fields.DateTime(description='创建时间'),
    "notes": fields.String(description='备注'),
    'creator': fields.Nested(simple_user_model),
    'file_size': fields.Integer(description='文件大小'),
    'file_size_str': fields.String(attribute=lambda ele: format_size(ele.file_size))
})

file_paginate_model = pagination_model.inherit('file_paginate_model', {
    "items": fields.Nested(file_model)
})

get_parser = base_get_parser.copy()

# 上传spu的图片
upload_file_parser = reqparse.RequestParser()
upload_file_parser.add_argument(
    'file_type', type=int, choices=[constant.FileType.IMG, constant.FileType.BINARY], required=True)
upload_file_parser.add_argument(
    'img_type', type=int, choices=[constant.ImgType.GALLERY, constant.ImgType.ELEMENT],
    default=constant.ImgType.GALLERY)

upload_file_parser.add_argument(
    'belong_type', type=str, choices=[constant.FileBelongType.SPU, constant.FileBelongType.SKU])
upload_file_parser.add_argument(
    'belong_id', type=int)


class FilesResource(Resource):
    @token_required
    @catch_error
    @api.expect(get_parser)
    def get(self):
        """
        3. 获取图库列表

        :return:
        """
        args = get_parser.parse_args()
        file_query = File.query.filter_by_company_id(file_type=constant.FileType.IMG, img_type=constant.ImgType.GALLERY)
        file_pagination = file_query.paginate(page=args['current_page'], per_page=args['per_page'])
        file_pagination = marshal(file_pagination, file_paginate_model)
        return ok(data=file_pagination,
                  message='get files success')

    @token_required
    @catch_error
    @api.expect(upload_file_parser)
    def post(self):
        """上传文件
            1. 上传spu图片
            1.2 上传sku图片
            1.3 上传图库
        """
        args = upload_file_parser.parse_args()
        file = request.files['file']

        if not file:
            return errors.BadRequestError('错误的文件')

        # 判断文件类型
        if '.' not in file.filename:
            raise errors.BadRequestError(error_msg='文件格式不正确')

        suffix = file.filename.rsplit('.', 1)[1].lower()
        if args.get('file_type') == constant.FileType.IMG:
            allowed_file = ALLOWED_IMG_EXTENSIONS
            if suffix not in allowed_file:
                raise errors.BadRequestError(error_msg='图片格式不正确')
        elif args.get('file_type') == constant.FileType.BINARY:
            allowed_file = ALLOWED_BINARY_EXTENSIONS
            if suffix not in allowed_file:
                raise errors.BadRequestError(error_msg='文件格式不正确')
        else:
            print('file_type', args.get('file_type'))
            raise errors.BadRequestError(error_msg='文件类型不正确')

        # 1 创建用户个人文件夹
        folder_path = os.path.join(
            current_app.root_path,
            constant.USER_FILE_BASE_FOLDER_PATH,
            str(g.user_id))
        if not os.path.exists(folder_path):
            os.makedirs(folder_path)

        # 2 保存文件
        time_str = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
        file_name, suffix = file.filename.split('.', 1)
        final_file_name = '{}_{}.{}'.format(file_name, time_str, suffix)
        file_path = os.path.join(folder_path, final_file_name)

        start_time = time.time()
        file.save(file_path)
        file.seek(0)
        end_time = time.time()
        print('文件保存时间：' + str(end_time - start_time) + " s")
        file_size = len(file.read())

        # 3 在数据库记一笔
        new_file = File.create(
            name=file.filename,
            file_path=file_path,
            file_type=args.get('file_type'),
            img_type=args.get('img_type'),
            file_size=file_size,
            commit=True,
        )

        # # 如果有所属关系
        if args.get("belong_type"):
            FileRelation.create(
                file_id=new_file.id,
                belong_type=args.get("belong_type"),
                belong_id=args.get("belong_id"),
            )
        db.session.commit()

        return ok(data=marshal(new_file, file_model), message="Upload file success")


delete_file_parser = reqparse.RequestParser()
delete_file_parser.add_argument('belong_id', type=int, help='属于那个对象', required=True)
delete_file_parser.add_argument('belong_type', type=str, help='对象类型', required=True,
                                choices=[constant.FileBelongType.SPU, constant.FileBelongType.SKU])


@api.route('/files/<int:file_id>/<file_name>')
class FileResource(Resource):
    @catch_error
    # @token_required
    def get(self, file_id, file_name):
        """获取文件"""
        file = File.query.filter_by(id=file_id).first()
        if not file:
            raise errors.BadRequestError(error_msg='文件不存在')

        if file.file_type == constant.FileType.BINARY:
            # todo 检查权限
            token_required_func()

            return send_file(file.file_path, as_attachment=True)
        elif file.file_type == constant.FileType.IMG:
            with open(file.file_path, 'rb') as f:
                image = f.read()
                resp = Response(image, mimetype="image/jpg")
                return resp
        else:
            return errors.BadRequestError('错误的文件类型')

    @catch_error
    @token_required
    def delete(self, obj_id, file_name):
        """删除文件"""
        args = delete_file_parser.parse_args()
        obj = File.query.filter_by(id=obj_id).first()
        if not obj:
            raise errors.BadRequestError(error_msg='文件不存在')

        FileRelation.query.filter_by(file_id=obj_id, belong_id=args.get('belong_id'),
                                     belong_type=args.get('belong_type')).delete()
        if args.get('belong_type') == constant.FileBelongType.SPU:
            OperationLog.create(
                operation_log_type=constant.OperationLogType.SPU,
                parent_id=args.get('belong_id'),
                action_str='删除SPU文件',
            )
        elif args.get('belong_type') == constant.FileBelongType.SKU:
            OperationLog.create(
                operation_log_type=constant.OperationLogType.SKU,
                parent_id=args.get('belong_id'),
                action_str='删除SKU文件',
            )
        else:
            raise errors.BadRequestError(error_msg='所属类型不正确')

        # 给文件打上删除标记
        obj.is_deleted = True
        db.session.commit()
        return ok(message='delete success')


# --------- route ----------
api.add_resource(FilesResource, '/files')  # 图库列表、上传图片
api.add_resource(FileResource, '/files/<int:file_id>/<file_name>')  # 获取文件

# 注册文档
# api.models[spu_model.name] = spu_model
